// 定义数组的方式
let arr1: number[] = [1, 2]
let arr2: Array<number> = arr1  //使用数组泛型 Array<元素类型>

// 初始化阶段声明了变量，中图更改为其他类型，会引发 TS 报错
export let num: string = 'lee'
// num = 1 报错

// 尖括号形式的类型断言
let oneString: any = 'this is a string'
let stringLength: number = (<string>oneString).length
console.log(stringLength) // 16
// as 形式的类型断言
let stringLength2: number = (oneString as string).length
console.log('stringLength2', stringLength2)

// 定义泛型函数
function hello<T> (arg: T) : T {
  return arg
}
// 调用泛型函数
// 第一种调用方法 尖括号
let output = hello<string>('hello TypeScript')
console.log('output', output)
// 第二种 类型推断
let output2 = hello('hello ts') // hello TypeScript
console.log('output2', output2) // hello ts


// 为什么需要类型保护   编译没问题，但是不能保证运行时没问题，这个运行时会有问题，但是能通过编译
interface Teacher {
  teach(): void
}
interface Student {
  learn(): void
}
function getPerson (): Teacher | Student {
  return {} as Teacher
}
const person = getPerson();
(<Teacher>person).teach();
(<Student>person).learn();

// 实现类型保护  下面的person is Teacher就是类型保护语句
function isTeacher (person: Teacher | Student): person is Teacher {
  return (<Teacher>person).teach !== undefined
}
if (isTeacher(person)) {
  person.teach()
} else {
  person.learn() 
}

// 类型保护案例2  使用typeof
function isNumber (padding: number | string): padding is number {
  return typeof padding === 'number'
}
function isString (padding: number | string): padding is string {
  return typeof padding === 'string'
}
function padLeft (padding: number | string) {
  if (isNumber(padding)) {
    console.log('i am a number ' + padding)
  }
  else {
    console.log('i am a string ' + padding)
  }
}
padLeft(10)
padLeft('lee')

// instanceof类型保护
interface Person1 {
  talk(): void
}
class Teacher1 implements Person1 {
  constructor (name: string) {}
  talk() {}
}
class Student1 implements Person1 {
  constructor (name: string, age: number, clasRoom: string) {}
  talk() {}
}
function getPerson1() {
  return Math.random() < 0.5 ?
          new Teacher1('张老师') :
          new Student1('小明', 8, '三班')
}
const person1 = getPerson1()
if (person1 instanceof Teacher1) {

}
if (person1 instanceof Student1) {

}

//type关键字创建一个 字面量类型结合联合类型使用
type Profession = 'teacher' | 'doctor' | 'accountant'
function createPerson (profession: Profession) {console.log(profession)}
createPerson('teacher')
createPerson('doctor')
// 当然数字字面量类型也是存在的
type NumType = 1 | 2 | 3
function getAget(): NumType {
  return 3
}
getAget()


// 类型映射
interface Person2 {
  name: string
  age: number
}
let p1: Person2 = {name: 'lee', age: 1}
// 映射为只读
type Person3 = Readonly<Person2>
let p2: Person3 = {name: 'lee', age: 2}
// p2.name = 'mon' // 报错，p2为只读的

type Person4 = Partial<Person3>
let p3: Person4 = {name: 'lee'} // 不用传age也不会报错，因为Person4所有属性都被映射为可选的了